/*
 * Copyright 2008-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.hasor.dataway.web;

import com.alibaba.druid.sql.SQLUtils;
import net.hasor.dataway.authorization.AuthorizationType;
import net.hasor.dataway.authorization.RefAuthorization;
import net.hasor.dataway.config.MappingToUrl;
import net.hasor.dataway.config.Result;
import net.hasor.dataway.dal.EntityDef;
import net.hasor.dataway.dal.FieldDef;
import net.hasor.dataway.utils.ZipUtils;
import net.hasor.utils.CommonCodeUtils;
import net.hasor.web.annotation.Get;
import net.hasor.web.objects.JsonRenderEngine;
import net.hasor.web.render.RenderType;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Api 备份
 *
 * @author aquanz
 * @version : 2020-12-14
 */
@MappingToUrl("/api/api-backup")
@RefAuthorization(AuthorizationType.ApiBackup)
@RenderType(value = "json", engineType = JsonRenderEngine.class)
public class ApiBackupController extends BasicController {
    protected static Logger logger = LoggerFactory.getLogger(ApiBackupController.class);
    public static SQLUtils.FormatOption DEFAULT_FORMAT_OPTION;

    @Get
    public Result<String> apiBackup() {
        try {
            String basicPath = ApiBackupController.class.getResource("/").toURI().getPath();
            String backSqlFilePath = basicPath + "/static/console/private/backup.sql";
            File sqlFile = new File(backSqlFilePath);
            String oldMd5 = "";
            if (!sqlFile.exists()) {
                FileUtils.forceMkdir(sqlFile.getParentFile());
            } else {
                oldMd5 = CommonCodeUtils.MD5.getMD5(
                        FileUtils.readFileToString(sqlFile, "utf-8"));
            }

            // 将两张表的数据查询出来，并生成 SQL 语句，最后打包成 zip 文件，并提供下载 URL
            String text = "";
            text += parseSqls(EntityDef.INFO);
            text += parseSqls(EntityDef.RELEASE);

            text = "/* backup file */\n\n" + text;
            String newMd5 = CommonCodeUtils.MD5.getMD5(text);

            if (newMd5.equals(oldMd5) && !newMd5.isEmpty()) {
                return Result.of(-90, "没有新数据需要备份");
            }

            // 写入 backup.sql
            FileUtils.writeStringToFile(sqlFile, text, "utf-8");

            // 压缩到 zip
            String date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String hash = newMd5.substring(0, 6);
            String zipFilename = "backup_" + date + "_" + hash + ".zip";
            String zipFilePath = basicPath + "/static/console/download/" + zipFilename;
            File zipFile = new File(zipFilePath);
            if (!zipFile.exists()) {
                FileUtils.forceMkdir(zipFile.getParentFile());
            }

            // 压缩
            ZipUtils.zipFile(sqlFile, zipFile);

            // 返回
            return Result.of("/console/download/" + zipFilename);
        } catch (Exception e) {
            logger.error("备份 API 表异常", e);
            return Result.of(-1, e.getMessage());
        }
    }

    /**
     * @param entityDef 定义的表
     * @return 入库语句
     */
    private String parseSqls(EntityDef entityDef) {
        List<Map<FieldDef, String>> infoList = this.dataAccessLayer.listObjectBy(//
                entityDef, //
                emptyCondition()//
        );
        infoList = (infoList == null) ? Collections.emptyList() : infoList;
        String table;
        if (entityDef.equals(EntityDef.INFO)) {
            table = "interface_info";
        } else if (entityDef.equals(EntityDef.RELEASE)) {
            table = "interface_release";
        } else {
            throw new RuntimeException("不支持的表类型");
        }
        table = quoteObj(table);
        StringBuilder sb = new StringBuilder("\n\ntruncate table " + table + ";\n\n");
        for (Map<FieldDef, String> row : infoList) {
            List<String> keys = new ArrayList<>();
            List<Object> vals = new ArrayList<>();
            row.forEach((key, val) -> {
                keys.add(quoteObj(key.name()));
                vals.add(val);
            });
            String keysJoin = StringUtils.join(keys, ",");
            String valsHolder = StringUtils.repeat(",?", keys.size()).substring(1);
            String sql = "insert into " + table + " (" + keysJoin + ") values(" + valsHolder + ");";
            sb.append(formattedSql(sql, vals)).append("\n");
        }
        return sb.toString();
    }

    /**
     * @param obj 数据库中的对象名
     * @return 添加引号之后的对象名
     */
    private String quoteObj(String obj) {
        return "`" + obj + "`";
    }

    /**
     * @param sql    值对象
     * @param params 对应 “?” 的参数
     * @return 转义后的值对象
     */
    private String formattedSql(String sql, List<Object> params) {
        return SQLUtils.format(sql, "mysql", params, DEFAULT_FORMAT_OPTION);
    }
}